#pragma once

namespace soy {

class CharacterSet {
	int size;
	bool valueAfter;
	bool* bset;
public:
	enum setBase {
		setNone = 0,
		setLower = 1,
		setUpper = 2,
		setDigits = 4,
		setAlpha = setLower | setUpper,
		setAlphaNum = setAlpha | setDigits
	};
	CharacterSet( setBase base = setNone, const char* initialSet = "", int size_ = 0x80, bool valueAfter_ = false ) {
		size = size_;
		valueAfter = valueAfter_;
		bset = new bool[size];
		for ( int i = 0; i < size; i++ ) {
			bset[i] = false;
		}
		AddString( initialSet );
		if ( base & setLower ) {
			AddString( "abcdefghijklmnopqrstuvwxyz" );
		}
		if ( base & setUpper ) {
			AddString( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" );
		}
		if ( base & setDigits ) {
			AddString( "0123456789" );
		}
	}
	~CharacterSet() {
		delete []bset;
		bset = 0;
		size = 0;
	}
	void Add( int val ) {
		assert( val >= 0 );
		assert( val < size );
		bset[val] = true;
	}
	void AddString( const char* setToAdd ) {
		for ( const char* cp = setToAdd; *cp; cp++ ) {
			int val = static_cast<unsigned char>( *cp );
			assert( val >= 0 );
			assert( val < size );
			bset[val] = true;
		}
	}
	bool Contains( int val ) const {
		assert( val >= 0 );
		if ( val < 0 ) {
			return false;
		}
		return ( val < size ) ? bset[val] : valueAfter;
	}
};

// Functions for classifying characters

inline bool IsASpace( int ch ) {
	return ( ch == ' ' ) || ( ( ch >= 0x09 ) && ( ch <= 0x0d ) );
}

inline bool IsASpaceOrTab( int ch ) {
	return ( ch == ' ' ) || ( ch == '\t' );
}

inline bool IsADigit( int ch ) {
	return ( ch >= '0' ) && ( ch <= '9' );
}

inline bool IsADigit( int ch, int base ) {
	if ( base <= 10 ) {
		return ( ch >= '0' ) && ( ch < '0' + base );
	} else {
		return ( ( ch >= '0' ) && ( ch <= '9' ) ) ||
			   ( ( ch >= 'A' ) && ( ch < 'A' + base - 10 ) ) ||
			   ( ( ch >= 'a' ) && ( ch < 'a' + base - 10 ) );
	}
}

inline bool IsASCII( int ch ) {
	return ch < 0x80;
}

inline bool IsAlphaNumeric( int ch ) {
	return
		( ( ch >= '0' ) && ( ch <= '9' ) ) ||
		( ( ch >= 'a' ) && ( ch <= 'z' ) ) ||
		( ( ch >= 'A' ) && ( ch <= 'Z' ) );
}

/**
 * Check if a character is a space.
 * This is ASCII specific but is safe with chars >= 0x80.
 */
inline bool isspacechar( int ch ) {
	return ( ch == ' ' ) || ( ( ch >= 0x09 ) && ( ch <= 0x0d ) );
}

inline bool iswordchar( int ch ) {
	return IsASCII( ch ) && ( IsAlphaNumeric( ch ) || ch == '.' || ch == '_' );
}

inline bool iswordstart( int ch ) {
	return IsASCII( ch ) && ( IsAlphaNumeric( ch ) || ch == '_' );
}

inline bool isoperator( int ch ) {
	if ( IsASCII( ch ) && IsAlphaNumeric( ch ) ) {
		return false;
	}
	if ( ch == '%' || ch == '^' || ch == '&' || ch == '*' ||
			ch == '(' || ch == ')' || ch == '-' || ch == '+' ||
			ch == '=' || ch == '|' || ch == '{' || ch == '}' ||
			ch == '[' || ch == ']' || ch == ':' || ch == ';' ||
			ch == '<' || ch == '>' || ch == ',' || ch == '/' ||
			ch == '?' || ch == '!' || ch == '.' || ch == '~' ) {
		return true;
	}
	return false;
}

// Simple case functions for ASCII.

inline char MakeUpperCase( char ch ) {
	if ( ch < 'a' || ch > 'z' ) {
		return ch;
	} else {
		return static_cast<char>( ch - 'a' + 'A' );
	}
}

int CompareCaseInsensitive( const char* a, const char* b );
int CompareNCaseInsensitive( const char* a, const char* b, size_t len );



};